/**
 *   @file MultiChildTree.cpp
 */
#include "MultiChildTree.h"
#include "MCTreeNode.h"
#include "../../common/CppMemPool.h"
#include "../../exceptions/BaseException.h"
#include "../../exceptions/RuntimeException.h"
#include "../../os/debug/Debug.h"
#include "../StackT.h"

using namespace cppflib::os;
using namespace cppflib::exceptions;

namespace cppflib
{

namespace collections
{

namespace tree
{

MultiChildTree::MultiChildTree(void)
{
   this->uniqueChildKeyOnly = false;
   this->pRootNode = NULL;
}

/**
 *  @param [in] uniqueChildKeyOnly -- true to allow children of the same parent with unique key only
 */
MultiChildTree::MultiChildTree(bool uniqueChildKeyOnly)
{
   this->uniqueChildKeyOnly = uniqueChildKeyOnly;
   this->pRootNode = NULL;
}

MultiChildTree::~MultiChildTree(void)
{
   FreeTree();
}

bool MultiChildTree::AllowUniqueChildKeyOnly(void)
{
   return uniqueChildKeyOnly;
}

/**
 *  @return true -- tree is empty
 *  @return false -- tree not empty
 */
bool MultiChildTree::IsTreeEmpty(void)
{
   return (pRootNode == NULL);
}

/**
 *  Get total number of nodes
 */
u32_t MultiChildTree::GetTotalNodeCount(void)
{
   if (pRootNode) {
      return 1 + pRootNode->GetAllChildCount();
   }

   return 0;
}

/**
 *  Get root node
 */
MCTreeNode * MultiChildTree::GetRootNode(void)
{
   return pRootNode;
}

/**
 *  Set root node
 *
 *  @return false if there is already one root node
 */
void MultiChildTree::SetRootNode(MCTreeNode * pRootNode)
{
   if (this->pRootNode) 
      throw RuntimeException(_S("Root node is already assigned"));

   this->pRootNode = pRootNode;
   this->pRootNode->SetAttachingTree(this);
   
   cppmempool::Retain(this->pRootNode);
}

/**
 *  Call back user provided function and catch exception thrown
 */
PRIVATE void __CallbackCatchException(MCTreeNode &node, const MCTreeWalkCallback &treeWalkCB)
{
   try {
      treeWalkCB(node);
   }
   catch (BaseException &e) {
      debug::Print(debug::DL_ERROR, _S("Traverse tree error: %s"), e.GetMsg());
   }
}

/**
 *  Traverse the tree from root to all its children one by one
 */
void MultiChildTree::TraverseTree(const MCTreeWalkCallback &treeWalkCB)
{
   if (pRootNode) {
      StackCppObjT<MCTreeNode> nodeStack(false); // traverse using a custom stack rather than a recursive function
      nodeStack.Push(pRootNode);

      while(nodeStack.GetCount() > 0) {
         MCTreeNode * pNode = nodeStack.Pop();
         __CallbackCatchException(*pNode, treeWalkCB); // call back here
         int childCount = static_cast<int>(pNode->GetDirectChildCount());
         for (int i = childCount - 1; i >= 0; --i)  // push children from tail to head
            nodeStack.Push(pNode->GetChild(i));
         cppmempool::Release(pNode);
      }    
   }
}


/**
 *  Free all nodes in this tree
 */
void MultiChildTree::FreeTree(void)
{
   if (pRootNode) {
      cppmempool::Release(pRootNode);
      pRootNode = NULL;
   }
}

}  // end of namespace tree

}  // end of namespace collections

}  // end of namespace cppflib
